fix(blocks): render the option label in a select trigger - #8
Conversation
Block Kit's select passed its options only as children, so Kumo had no value-to-label map to hand Base UI. The trigger serialized the raw value, and rendered nothing at all when the value was "", null or absent, since Base UI counts an empty string as "no value". Pass the options as `items` too, which is the documented way to make the selected value render as a label, and add an optional `placeholder` for the unselected state, mirroring Kumo's own prop. The placeholder defaults to the label of an option whose value is "" -- the usual "All ..." filter entry, which Base UI would otherwise hide behind the placeholder -- and to "Select..." when there is none. The popup still renders the same children, so option order, keys and the submitted values are unchanged.
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
|
This PR has been inactive for 14 days. It will be closed automatically in 7 days if there is no further activity. If you're still working on this, please push an update or leave a comment. |
What does this PR do?
A Block Kit
selectrendered the raw option value in its closed trigger instead of the option's label, and rendered completely empty when the value was"",nullor absent.Root cause, in two halves:
packages/blocks/src/elements/select.tsxpassed the options only asSelect.Optionchildren — noitems,placeholderorrenderValue. Kumo 2.6 wraps Base UI 1.5 (not Radix), and Base UI'sSelect.Valueonly resolves a label when the root hasitems: without it,resolveSelectedLabel(value, undefined, undefined)falls through toserializeValue(value), i.e. the value itself.""as "no value" (hasSelectedValueruns the value throughserializeValueand compares to""), so a select whose value was an empty string rendered a blank trigger. With noplaceholdereither, an unset value was blank too.The fix:
itemsas well. That is Base UI's documented mechanism — "When specified,<Select.Value>renders the label of the selected item instead of the raw value" — and it is what the admin's ownDynamicSelectalready does.itemsonly feeds label resolution in the store; it does not touch selection semantics. The children still render the popup, so option order, keys and DOM are unchanged.placeholdertoSelectElement, mirroring Kumo'sSelectprop and the existingcombobox/text_input/date_inputfields (type,validation.ts,elements.select()builder, docs).valueis"", and otherwise toSelect.... Because Base UI treats""and "unset" as the same display state, they must share one string; the common{ value: "", label: "All statuses" }filter entry is the author's own empty-state label, so it wins over an invented default. An explicitplaceholderalways wins over both.Backward compatibility: display-only. Submitted values are untouched — including an empty-string option value, which is still selectable and still submits
""(verified by test and in the browser). A select that previously showed a blank trigger now shows a label or placeholder; nothing about the payload changes.Scoped out, after checking:
comboboxalready passesitemsand uses the option object as its value, so Base UI resolvesvalue.label— verified rendering "Published" forvalue: "pub_1".radiohas no selected-value display surface at all (everyRadio.Itemcarries its ownlabel), so the root cause cannot reach it. Neither needed a change.Closes #
Type of change
Checklist
pnpm typecheckpassespnpm lintpasses (pnpm lint:json→ 0 diagnostics)pnpm testpasses (all packages;@emdash-cms/blocks106 tests,@emdash-cms/admin1249 tests)pnpm formathas been run (oxfmt --checkclean)packages/blockshas no Lingui setup; the new default follows the neighbouringcombobox's literal"Search...". Localising Block Kit element strings is a separate, package-wide change.placeholderfield is the additive minimum needed to fix the empty trigger.AI-generated code disclosure
Screenshots / test output
New suite
packages/blocks/tests/select-element.test.tsxrenders the real KumoSelect(no mock — a mockedSelectcannot exhibit the bug). Againstmain6 of its 9 tests fail; the 3 value-submission tests pass both before and after, which is the backward-compatibility check:After the fix, all 9 pass (
pnpm --filter @emdash-cms/blocks test: 4 files, 106 tests).Also exercised in a real browser (Chromium, blocks playground). Trigger text vs. the value in the hidden form input:
initial_value: "pub_1"Publishedpub_1{ value: "", label: "All statuses" },initial_value: ""All statuses""initial_value, noplaceholderSelect...""placeholder: "Any status"Any status""The existing SEO template's select in the playground shows
None (extractive)where it previously showednone.